package com.liu.stockmarket.ui.statistics

import android.graphics.Color
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.formatter.DefaultValueFormatter
import com.github.mikephil.charting.formatter.PercentFormatter
import com.github.mikephil.charting.formatter.ValueFormatter
import com.liu.stockmarket.BaseFragment
import com.liu.stockmarket.MainViewModel
import com.liu.stockmarket.R
import com.liu.stockmarket.StockMarketApplication
import com.liu.stockmarket.databinding.FragmentStatisticsBinding
import com.liu.stockmarket.model.Stock
import com.liu.stockmarket.mpchart.MyYAxisRenderer
import com.liu.stockmarket.repository.database.entity.ProfitEntity
import com.liu.stockmarket.ui.detail.DetailActivity
import com.liu.stockmarket.ui.statistics.adapter.CalendarAdapter
import com.liu.stockmarket.ui.statistics.adapter.HistoryAdapter
import com.liu.stockmarket.ui.statistics.adapter.StatisticsAdapter
import com.liu.stockmarket.ui.weight.TipMarkerView
import com.liu.stockmarket.utils.*
import com.lxj.xpopupext.popup.TimePickerPopup
import kotlinx.coroutines.ExperimentalCoroutinesApi
import okhttp3.internal.immutableListOf
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Year
import java.time.ZoneOffset

@ExperimentalCoroutinesApi
class StatisticsFragment : BaseFragment() {

    private lateinit var statisticsViewModel: StatisticsViewModel
    private var _binding: FragmentStatisticsBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    private lateinit var statisticsAdapter: StatisticsAdapter

    private lateinit var historyAdapter: HistoryAdapter

    private lateinit var calendarAdapter: CalendarAdapter

    private val mainViewModel: MainViewModel by activityViewModels()

    /**
     * 用户是否设置了开始韭菜时间，如果没有就使用第一次交易时间
     */
    private var hasSettingPlayDate = false

    private val historyData = ArrayList<HistoryAdapter.Item>()
    private var historyPage = 0

    /**
     * 日历视图查询时间
     */
    private var selectedDate = LocalDate.now()

    /**
     * 所有盈亏记录，为了防止数据量过多，或许需要优化
     */
    private var allProfits: List<ProfitEntity> = ArrayList()

    override fun initView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        val stockMarketApplication = this.requireActivity().application as StockMarketApplication

        statisticsViewModel =
            ViewModelProvider(
                this, StatisticsViewModel.StatisticsViewModelFactory(
                    stockMarketApplication.transactionRecordRepository,
                    stockMarketApplication.stockRepository,
                    stockMarketApplication.profitRepository,
                    stockMarketApplication.easyMoneyRepository
                )
            ).get(StatisticsViewModel::class.java)

        _binding = FragmentStatisticsBinding.inflate(inflater, container, false)
        val root: View = binding.root



        statisticsAdapter = StatisticsAdapter()
        historyAdapter = HistoryAdapter()
        calendarAdapter = CalendarAdapter()

        with(binding.listStatistics) {
            adapter = statisticsAdapter
        }

        with(binding.listHistory) {
            adapter = historyAdapter
//            addItemDecoration(
//                DividerItemDecoration(
//                    this@StatisticsFragment.requireContext(),
//                    DividerItemDecoration.VERTICAL
//                )
//            )

        }
        with(binding.listCalendar) {
            adapter = calendarAdapter
        }




        binding.swipeLayout.setColorSchemeColors(Color.argb(100, 255, 152, 0))
        initLineChart()
        binding.llContent.visibility = View.GONE

        val playDate = PreferenceManager.getDefaultSharedPreferences(this.requireContext())
            .getString(getString(R.string.setting_play_date), null)

        initPayDay(playDate)

        return root
    }

    private fun yearView(selectedDate: LocalDate) {
        binding.llSelectDate.visibility = View.GONE
        binding.listCalendar.layoutManager = GridLayoutManager(requireContext(), 4)
        calendarAdapter.setList(parseCalendarProfitData(selectedDate))
    }

    /**
     * 月视图
     */
    private fun monthView(selectedDate: LocalDate) {
        binding.llSelectDate.visibility = View.VISIBLE
        binding.tvSelectDate.text = selectedDate.year.toString()
        binding.listCalendar.layoutManager = GridLayoutManager(requireContext(), 4)
        calendarAdapter.setList(parseCalendarProfitData(selectedDate))
    }

    /**
     * 日视图
     */
    private val monthTitle = immutableListOf("日", "一", "二", "三", "四", "五", "六")

    private val offset = mapOf(7 to 0, 1 to 1, 2 to 2, 3 to 3, 4 to 4, 5 to 5, 6 to 6)

    private fun dayView(date: LocalDate) {
        binding.llSelectDate.visibility = View.VISIBLE
        binding.tvSelectDate.text = date.YM()
        binding.listCalendar.layoutManager = GridLayoutManager(requireContext(), 7)

        calendarAdapter.setList(parseCalendarProfitData(date))
    }


    private fun parseCalendarProfitData(date: LocalDate): List<CalendarAdapter.Item> {
        when {
            isYearView() -> {
                val sumOf = allProfits.filter { pe ->
                    pe.stockType == Stock.StockType.ALL
                }.sumOf { pe -> pe.todayProfit }


                binding.tvCalendarTotalProfit.money(this.requireContext(), sumOf)
                return allProfits.filter { pe -> pe.stockType == Stock.StockType.ALL }
                    .groupBy { pe -> pe.date.year }
                    .map {
                        CalendarAdapter.Item(
                            isTitle = false,
                            isShow = true,
                            title = it.key.toString(),
                            date = LocalDate.of(it.key, 1, 1),
                            profit = it.value.sumOf { pe -> pe.todayProfit }
                        )
                    }
            }
            isMonthView() -> {
                val sumOf = allProfits.filter { pe ->
                    pe.stockType == Stock.StockType.ALL && pe.date.year == date.year
                }.sumOf { pe -> pe.todayProfit }


                binding.tvCalendarTotalProfit.money(this.requireContext(), sumOf)
                return (1..12).map {
                    val nextDate = selectedDate.withMonth(it).withDayOfMonth(1)

                    val monthProfit = allProfits.filter { pe ->
                        pe.stockType == Stock.StockType.ALL && pe.date.withDayOfMonth(1)
                            .equals(nextDate)
                    }.sumOf { pe -> pe.todayProfit }


                    CalendarAdapter.Item(
                        isTitle = false,
                        isShow = true,
                        title = it.toString(),
                        date = nextDate,
                        profit = monthProfit
                    )
                }
            }
            else -> {
                val sumOf = allProfits.filter { pe ->
                    pe.stockType == Stock.StockType.ALL && (pe.date.year == date.year && pe.date.monthValue == date.monthValue)
                }.sumOf { pe -> pe.todayProfit }


                binding.tvCalendarTotalProfit.money(this.requireContext(), sumOf)
                val days = ArrayList<CalendarAdapter.Item>()
                // 添加标题
                days.addAll(monthTitle.map {
                    CalendarAdapter.Item(
                        isTitle = true, isShow = true, title = it
                    )
                })

                val firstDay = LocalDate.of(date.year, date.month, 1)
                val dayOfWeek = firstDay.dayOfWeek.value

                if (dayOfWeek != 7) {
                    days.addAll((1..offset[dayOfWeek]!!).map {
                        CalendarAdapter.Item(
                            isTitle = false, isShow = false
                        )
                    })
                }


                days.addAll((1..date.lengthOfMonth()).map {
                    val nextDate = selectedDate.withDayOfMonth(it)
                    val dayProfit = allProfits.filter { pe ->
                        pe.stockType == Stock.StockType.ALL && pe.date == nextDate
                    }.sumOf { pe -> pe.todayProfit }
                    CalendarAdapter.Item(
                        isTitle = false,
                        isShow = true,
                        title = it.toString(),
                        date = nextDate,
                        profit = dayProfit
                    )
                })
                return days
            }
        }
    }

    private fun initPayDay(playDate: String?) {
        playDate?.let {
            hasSettingPlayDate = true
            val timeInterval = LocalDate.parse(playDate).timeInterval(LocalDate.now())
            binding.tvPlayDay.text = getString(R.string.play_day, timeInterval)
        }
    }

    override fun listener() {
        historyAdapter.setOnItemClickListener { adapter, _, position ->
            val item = adapter.getItem(position) as HistoryAdapter.Item
            DetailActivity.start(this.requireContext(), item.stockId)
        }

        binding.swipeLayout.setOnRefreshListener {
            loadData()
        }

        binding.scrollView.setOnScrollChangeListener { v, _, scrollY, _, oldScrollY ->
            val rect = Rect()
            v.getHitRect(rect);
            binding.topTip.visibility =
                if (!binding.tableHead.llTableHead.getLocalVisibleRect(rect)
                    && binding.listHistory.getLocalVisibleRect(rect)
                ) {
                    // 子控件可见(部分可见 全部可见)
                    View.VISIBLE
                } else {
                    // 子控件完全不在可视范围内
                    View.GONE
                }
            mainViewModel.setShowDataCenterButton(scrollY <= oldScrollY)
        }

        binding.tvSaveTodayProfit.setOnClickListener {
            this.requireContext()
                .confirm("保存今日收益", "由于基金一般在晚上10点后确认完净值，所以建议晚上10点后再来保存，将替换当日的上一次保存", {
                    if (statisticsViewModel.saveTodayProfit()) {
                        loadData()
                        this.requireContext().toast("保存了")
                    }

                })

        }

        binding.tabShowType.onTabSelected {
            statisticsViewModel.profitLineChartData(it.position)
            setYValueFormatter(it.position == 1 || it.position == 2)
            setMarkerView(it.position == 1 || it.position == 2, it.position == 2)
        }
        historyAdapter.loadMoreModule.setOnLoadMoreListener {
            addHistoryData()
        }

        binding.tabCalendarType.onTabSelected {
            // 0：年，1:月
            when (it.position) {
                0 -> {
                    yearView(selectedDate)
                }
                1 -> {
                    monthView(selectedDate)
                }
                else -> {
                    dayView(selectedDate)
                }
            }
        }

        calendarAdapter.setOnItemClickListener { adapter, _, position ->
            val item = adapter.getItem(position) as CalendarAdapter.Item
            selectedDate = item.date
            if (binding.tabCalendarType.selectedTabPosition == 0) {
                // 年视图跳转到月视图
                binding.tabCalendarType.selectByPosition(1)
            } else if (binding.tabCalendarType.selectedTabPosition == 1) {
                // 月视图跳转到日视图
                binding.tabCalendarType.selectByPosition(2)
            }


        }

        binding.tvSelectDate.setOnClickListener {
            requireContext().selectDate(if (isMonthView()) TimePickerPopup.Mode.Y else TimePickerPopup.Mode.YM) {
                selectedDate = it.toLocalDate()
                if (isMonthView()) {
                    monthView(selectedDate)
                } else if (isDayView()) {
                    dayView(selectedDate)
                }
            }
        }

        // 前一年或前一个月
        binding.ivDateBefore.setOnClickListener {
            if (isMonthView()) {
                selectedDate = selectedDate.minusYears(1)
                monthView(selectedDate)
            } else if (isDayView()) {
                selectedDate = selectedDate.minusMonths(1)
                dayView(selectedDate)
            }

        }
        // 后一年或后一个月
        binding.ivDateAfter.setOnClickListener {
            if (isMonthView()) {
                selectedDate = selectedDate.plusYears(1)
                monthView(selectedDate)
            } else if (isDayView()) {
                selectedDate = selectedDate.plusMonths(1)
                dayView(selectedDate)
            }
        }


    }

    /**
     * 是否是年视图
     */
    private fun isYearView(): Boolean {
        return binding.tabCalendarType.selectedTabPosition == 0
    }

    private fun isMonthView(): Boolean {
        return binding.tabCalendarType.selectedTabPosition == 1
    }

    private fun isDayView(): Boolean {
        return binding.tabCalendarType.selectedTabPosition == 2
    }

    private fun loadData() {
        historyPage = 0
        statisticsViewModel.statistics()
        statisticsViewModel.profitLineChartData(binding.tabShowType.selectedTabPosition)
    }

    override fun observer() {
        statisticsViewModel.statisticsData.observe(this) {
            binding.swipeLayout.isRefreshing = false
            binding.llContent.visibility = View.VISIBLE


            if (!hasSettingPlayDate) {
                binding.tvPlayDay.text = getString(R.string.play_day, it.playDay)
            }

            binding.tvAllHoldingMoney.text = it.totalMoney.getString()
            statisticsAdapter.setList(it.statistics)

            // 为了解决数据量过大，造成刷新按钮卡顿问题，原因未知。
            historyData.clear()
            historyAdapter.data.clear()
            historyData.addAll(it.historys)
            addHistoryData()

            binding.chipProfit.text = getString(R.string.profit, it.profitCount)
            binding.chipFair.text = getString(R.string.fair, it.fairCount)
            binding.chipDeficit.text = getString(R.string.deficit, it.deficitCount)


        }

        statisticsViewModel.profitData.observe(this) {

            setLineData(it)

        }

        statisticsViewModel.allProfits.observe(this) {
            allProfits = it
            when {
                isYearView() -> {
                    yearView(selectedDate)
                }
                isMonthView() -> {
                    monthView(selectedDate)
                }
                else -> {
                    dayView(selectedDate)
                }
            }

        }

    }

    private fun addHistoryData() {
        val fromIndex = historyPage++ * 10
        var endIndex = fromIndex + 10

        if (endIndex >= historyData.size) {
            historyAdapter.loadMoreModule.isEnableLoadMore = false
            endIndex = historyData.size
        } else {
            historyAdapter.loadMoreModule.isEnableLoadMore = true
        }

        historyAdapter.addData(historyData.subList(fromIndex, endIndex))
        historyAdapter.loadMoreModule.loadMoreComplete()
    }


    /**
     * 设置数据
     */
    private fun setLineData(map: Map<Stock.StockType, List<Entry>>) {
        val lineChart = binding.lineChart
        if (map.isNullOrEmpty()) {
            return
        }

        val dataSets = map.map {
            val dataSet = LineDataSet(it.value, it.key.text)
            dataSet.color = when (it.key) {
                Stock.StockType.STOCK -> this.requireContext().getColor(R.color.line_stock)
                Stock.StockType.BOND -> this.requireContext().getColor(R.color.line_bond)
                Stock.StockType.FUND -> this.requireContext().getColor(R.color.line_fund)
                Stock.StockType.ALL -> this.requireContext().getColor(R.color.line_all)
                else -> this.requireContext().getColor(R.color.color_primary)
            }
            dataSet.lineWidth = 2.8f

            dataSet.setDrawValues(false)
            dataSet
        }

        val xAxis = lineChart.xAxis
        if (dataSets[0].entryCount < 5) {
            xAxis.setLabelCount(dataSets[0].entryCount, true)
        }

        val lineData = LineData(dataSets)
        lineChart.data = lineData
        lineChart.invalidate()
    }

    private fun initLineChart() {
        val lineChart = binding.lineChart
        val xAxis = lineChart.xAxis
        val yAxis = lineChart.axisLeft


        lineChart.setNoDataText("没有数据")

        with(xAxis) {
            position = XAxis.XAxisPosition.BOTTOM


            setAvoidFirstLastClipping(true)
            valueFormatter = object : ValueFormatter() {
                override fun getFormattedValue(value: Float): String {
                    val date = LocalDateTime.ofEpochSecond(value.toLong(), 0, ZoneOffset.UTC)
                    return date.MD()
                }
            }
            textColor = this@StatisticsFragment.requireContext().getColor(R.color.black)

        }

        with(lineChart.legend) {
            this.textColor = this@StatisticsFragment.requireContext().getColor(R.color.black)
        }


        with(yAxis) {
            textColor = this@StatisticsFragment.requireContext().getColor(R.color.black)

        }


        lineChart.setPinchZoom(true)


        lineChart.setDrawMarkers(true)
        // 默认
        setMarkerView(false)
        lineChart.rendererLeftYAxis = MyYAxisRenderer(
            requireContext(),
            lineChart.viewPortHandler,
            lineChart.axisLeft,
            lineChart.getTransformer(YAxis.AxisDependency.LEFT)
        )


        lineChart.axisRight.isEnabled = false
        lineChart.description.isEnabled = false
    }

    /**
     * @param isPercent 是否是百分比
     */
    private fun setMarkerView(isPercent: Boolean = false, isTodayProfit: Boolean = false) {
        val lineChart = binding.lineChart
        val tipMarkerView = TipMarkerView(this.requireContext()) { e ->
            val profitEntity = e.data as ProfitEntity
            val date = LocalDateTime.ofEpochSecond(e.x.toLong(), 0, ZoneOffset.UTC)
            val value = when {
                isPercent and !isTodayProfit -> {
                    "${e.y}%"
                }
                isTodayProfit -> {
                    "收益率：${e.y}%\n收益：${profitEntity.todayProfit.getString()}"
                }
                else -> {
                    val v = BigDecimal(e.y.toString()).getString(2)
                    v
                }
            }
            return@TipMarkerView "${profitEntity.stockType.text}\n${date.YMD()}\n${value}"
        }
        tipMarkerView.chartView = lineChart
        lineChart.marker = tipMarkerView
    }

    private fun setYValueFormatter(isPercent: Boolean) {
        val lineChart = binding.lineChart
        with(lineChart.axisLeft) {
            if (isPercent) {
                this.valueFormatter = PercentFormatter()
            } else {
                this.valueFormatter = DefaultValueFormatter(2)
            }
        }
    }


    override fun onResume() {
        super.onResume()
        binding.swipeLayout.isRefreshing = true
        loadData()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}